using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace CHIP8Emulator {
    public abstract class Logger {
        /*
         * An abstract logger. Provides an interface to log messages to File, Console, LogControl, or any combination of them.
         */

        public abstract void Log(LogMessageType messageType, string format, params object[] args);

        public void Log(Exception ex){
            Log(LogMessageType.Error, Ex2String(ex));
        }


        protected static void Log(TextWriter writer, LogMessageType messageType, string format, object[] args) {
            writer.WriteLine(string.Format("{0}: {1}: {2}", DateTime.Now, messageType, string.Format(format, args)));
        }

        protected static string Ex2String(Exception ex) {
            return new StringBuilder()
                .AppendLine(string.Format("Exception"))
                .AppendLine(string.Format("Message: {0}", ex.Message))
                .AppendLine(string.Format("StackTrace:\n{0}", ex.StackTrace)).ToString();
        }
    }

    public class CompositeLogger : Logger {
        /*
         * Compisite logger. Delegates logging to inner loggers.
         */

        private readonly List<Logger> _loggers = new List<Logger>();

        public override void Log(LogMessageType messageType, string format, params object[] args) {
            foreach (var logger in _loggers) {
                logger.Log(messageType, format, args);
            }
        }

        public CompositeLogger Add(Logger logger) {
            _loggers.Add(logger);
            return this;
        }
    }

    public class FileLogger : Logger {
        /*
         * Log messages to file. Thread-safe.         
         */

        private static Dictionary<string, object> LOCK_OBJECT = new Dictionary<string, object>();
        private readonly string _filename;

        public FileLogger(string filename) {
            _filename = filename;
        }

        public override void Log(LogMessageType messageType, string format, params object[] args) {
            lock (GetLockObject(_filename)) {
                using (TextWriter writer = new StreamWriter(new FileStream(_filename, FileMode.Append))) {
                    Log(writer, messageType, format, args);
                    writer.Close();
                }
            }
        }

        private object GetLockObject(string filename){
            if (!LOCK_OBJECT.ContainsKey(filename))
                LOCK_OBJECT[filename] = new object();
            return LOCK_OBJECT[filename];
        }
    }

    public enum LogMessageType {
        Info,
        Warning,
        Error,
    }
}